//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
using Microsoft.Research.ScientificWorkflow.TridentModel;
using Microsoft.Research.ScientificWorkflow.TridentComposer;

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    public class TridentActivityConnectionVisitor
    {
        private class ConnectionNode
        {
            public ITridentUIElement Left { get; set; }
            public ITridentUIElement Right { get; set; }
        }

        private Canvas canvas;
        private WorkflowComposer workflowComposer;
        private Stack<ICompositeUIElement> parents = new Stack<ICompositeUIElement>();
        private Dictionary<WorkflowActivityConnectionElement, ConnectionNode> connectionNodes = new Dictionary<WorkflowActivityConnectionElement, ConnectionNode>();
        private ComposerRenderer composerRenderHelper;
        private ICompositeUIElement rootElement;

        public TridentActivityConnectionVisitor(Canvas canvas, WorkflowComposer workflowComposer, ComposerRenderer composerRenderHelper)
        {
            this.canvas = canvas;
            this.workflowComposer = workflowComposer;
            this.composerRenderHelper = composerRenderHelper;
        }

        public void GridLayoutUpdated()
        {
            this.UpdatePointPositions();
        }

        public void CreateActivityConnections(ICompositeUIElement root)
        {
            if (root == null)
            {
                return;
            }

            this.rootElement = root;
            this.FindParents(this.rootElement);
            this.CreateCompoisteActivityConnection(true);
        }

        public void InsertActivityConnection()
        {
            this.RefreshAllConnections();
        }

        private void CreateCompoisteActivityConnection(bool workflowRoot)
        {
            while (this.parents.Count != 0)
            {
                ICompositeUIElement parent = this.parents.Pop();
                bool isRootElement = workflowRoot && this.parents.Count == 0;
                string activityClass = (parent as ITridentUIElement).Model.ActivityClass;
                if (activityClass == UIDesignerCodeConstants.ACTIVITYCLASSPARALLEL ||
                    activityClass == UIDesignerCodeConstants.ACTIVITYCLASSREPLICATOR ||
                    activityClass == UIDesignerCodeConstants.ACTIVITYCLASSFOR ||
                    activityClass == UIDesignerCodeConstants.ACTIVITYCLASSIFCONDITION)
                {
                    // Note: Do not draw activity connections for the above elements
                }
                else if (parent as SubsectionCollapsedCtrl != null)
                {
                    // Note: Do nothing for subsection collapsed control
                }
                else
                {
                    this.CreateChildConnections(parent, isRootElement);
                }
            }
        }

        public void DeleteTridentUIElement()
        {
            this.RefreshAllConnections();
        }

        private void RefreshAllConnections()
        {
            this.ClearActivityConnectionElements();
            this.CreateActivityConnections(this.rootElement);
            this.composerRenderHelper.RefreshUI();
        }

        private void FindParents(ICompositeUIElement root)
        {
            this.parents.Push(root);
            foreach (ITridentUIElement child in root.ChildElements)
            {
                ICompositeUIElement composite = child as ICompositeUIElement;
                if (composite != null)
                {
                    this.FindParents(composite);
                }
            }
        }

        private void CreateChildConnections(ICompositeUIElement parent, bool workflowRoot)
        {
            Queue<ITridentUIElement> queue = new Queue<ITridentUIElement>();

            // Add the start element
            StartElement startElement = InsertStartElement(parent, workflowRoot);
            queue.Enqueue(startElement);

            // for all the child elements, add both left and right flow-connection elements.
            foreach (ITridentUIElement child in parent.ChildElements)
            {
                queue.Enqueue(child);
            }

            // Add the end element
            EndElement endElement = InsertEndElement(parent, workflowRoot);
            queue.Enqueue(endElement);

            while (queue.Count > 1)
            {
                ITridentUIElement left = queue.Dequeue();
                ITridentUIElement right = queue.Peek();

                BaseCompositeModel baseCompositeModel = (parent as ITridentUIElement).Model as BaseCompositeModel;
                WorkflowActivityConnectionElement connectionElement = new WorkflowActivityConnectionElement(left, right, baseCompositeModel, workflowComposer);
                connectionElement.CreateSubsection += new EventHandler<SubsectionCreationEventArgs>(this.composerRenderHelper.OnCreateNewSubsection);
                this.canvas.Children.Add(connectionElement);
                this.connectionNodes.Add(connectionElement, new ConnectionNode { Left = left, Right = right });
            }
        }

        private StartElement InsertStartElement(ICompositeUIElement parent, bool workflowRoot)
        {
            ITridentUIElement parentTridentUiElem = parent as ITridentUIElement;
            StartElement startElement = new StartElement(workflowRoot);
            startElement.Margin = new Thickness(30, 17, 30, 0);
            startElement.VerticalAlignment = workflowRoot ? VerticalAlignment.Center : VerticalAlignment.Top;
            bool notASubsectionCollapsedControl = (parentTridentUiElem as SubsectionCollapsedCtrl) == null;
            if (notASubsectionCollapsedControl)
            {
                parent.MainContentHolder.Children.Insert(0, startElement);
            }

            if (!workflowRoot)
            {
                CompositeActivityConnector compositeActivityConnector = new CompositeActivityConnector(parentTridentUiElem, startElement);
                Canvas.SetLeft(compositeActivityConnector, 0);
                Canvas.SetTop(compositeActivityConnector, 0);
                compositeActivityConnector.UpdatePointPositions();
                this.canvas.Children.Add(compositeActivityConnector);
            }

            return startElement;
        }

        private EndElement InsertEndElement(ICompositeUIElement parent, bool workflowRoot)
        {
            ITridentUIElement parentTridentUiElem = parent as ITridentUIElement;
            EndElement endElement = new EndElement(workflowRoot);
            endElement.Margin = new Thickness(30, 17, 30, 0);
            endElement.VerticalAlignment = workflowRoot ? VerticalAlignment.Center : VerticalAlignment.Top;
            bool notASubsectionCollapsedControl = (parentTridentUiElem as SubsectionCollapsedCtrl) == null;
            if (notASubsectionCollapsedControl)
            {
                parent.MainContentHolder.Children.Add(endElement);
            }

            if (!workflowRoot)
            {
                CompositeActivityConnector compositeActivityConnector = new CompositeActivityConnector(parentTridentUiElem, endElement);
                Canvas.SetLeft(compositeActivityConnector, 0);
                Canvas.SetTop(compositeActivityConnector, 0);
                compositeActivityConnector.UpdatePointPositions();
                this.canvas.Children.Add(compositeActivityConnector);
            }

            return endElement;
        }

        private void UpdatePointPositions()
        {
            foreach (UIElement element in this.canvas.Children)
            {
                IUpdatePointPositions updator = element as IUpdatePointPositions;
                if (updator != null)
                {
                    updator.UpdatePointPositions();
                }
            }
        }

        private void ClearActivityConnectionElements()
        {
            this.canvas.Children.Clear();
            this.connectionNodes.Clear();

            this.FindParents(this.rootElement);
            while (parents.Count != 0)
            {
                ICompositeUIElement parent = parents.Pop();
                TridentActivityConnectionVisitor.ClearChildStartEndElements(parent);
            }
        }

        private static void ClearChildStartEndElements(ICompositeUIElement parent)
        {
            int count = parent.MainContentHolder.Children.Count;
            if (count >= 2)
            {
                if (parent.MainContentHolder.Children[count - 1] as EndElement != null)
                {
                    parent.MainContentHolder.Children.RemoveAt(count - 1);
                }

                if (parent.MainContentHolder.Children[0] as StartElement != null)
                {
                    parent.MainContentHolder.Children.RemoveAt(0);
                }
            }
        }
    }
}
